/* eslint-disable max-statements */ import type { MDXComponents } from 'mdx/types'; import type { GetStaticPaths, GetStaticProps } from 'next'; import dynamic from 'next/dynamic'; import Head from 'next/head'; import NextImage from 'next/image'; import { useRouter } from 'next/router'; import Script from 'next/script'; import type { ComponentType } from 'react'; import { useIntl } from 'react-intl'; import { getLayout, SharingWidget, Spinner, Heading, ProjectOverview, type ProjectMeta, type Repository, Page, PageHeader, PageSidebar, TocWidget, PageBody, } from '../../components'; import { mdxComponents } from '../../components/mdx'; import styles from '../../styles/pages/project.module.scss'; import type { NextPageWithLayout, ProjectPreview, Repos } from '../../types'; import { CONFIG } from '../../utils/config'; import { ROUTES } from '../../utils/constants'; import { getSchemaJson, getSinglePageSchema, getWebPageSchema, } from '../../utils/helpers'; import { getProjectData, getProjectFilenames, loadTranslation, type Messages, } from '../../utils/helpers/server'; import { useBreadcrumb, useGithubApi, useHeadingsTree, } from '../../utils/hooks'; type ProjectPageProps = { project: ProjectPreview; translation: Messages; }; /** * Project page. */ const ProjectPage: NextPageWithLayout = ({ project }) => { const { id, intro, meta, title } = project; const { cover, dates, license, repos, seo, technologies } = meta; const intl = useIntl(); const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({ title, url: `${ROUTES.PROJECTS}/${id}`, }); const { ref, tree } = useHeadingsTree({ fromLevel: 2 }); const ProjectContent: ComponentType = dynamic( async () => import(`../../content/projects/${id}.mdx`), { loading: () => , } ); const { asPath } = useRouter(); const page = { title: `${seo.title} - ${CONFIG.name}`, url: `${CONFIG.url}${asPath}`, }; /** * Retrieve the project repositories. * * @param {Repos} repositories - A repositories object. * @returns {Repository[]} - An array of repositories. */ const getRepos = (repositories: Repos): Repository[] => { const definedRepos: Repository[] = []; if (repositories.github) definedRepos.push({ id: 'Github', label: intl.formatMessage({ defaultMessage: 'Github profile', description: 'ProjectsPage: Github profile link', id: 'Nx8Jo5', }), url: repositories.github, }); if (repositories.gitlab) definedRepos.push({ id: 'Gitlab', label: intl.formatMessage({ defaultMessage: 'Gitlab profile', description: 'ProjectsPage: Gitlab profile link', id: 'sECHDg', }), url: repositories.gitlab, }); return definedRepos; }; const loadingRepoPopularity = intl.formatMessage({ defaultMessage: 'Loading the repository popularity...', description: 'ProjectsPage: loading repository popularity', id: 'RwI3B9', }); const { isError, isLoading, data } = useGithubApi( /* * Repo should be defined for each project so for now it is safe for my * use-case. However, I should refactored it to handle cases where it is * not defined. The logic should be extracted in another component I think. * * TODO: fix this hardly readable argument */ meta.repos ? meta.repos.github ?? '' : '' ); if (isError) return 'Error'; if (isLoading || !data) return ; const overviewMeta: Partial = { creationDate: data.created_at, lastUpdateDate: data.updated_at, license, popularity: repos?.github ? { count: data.stargazers_count, url: `https://github.com/${repos.github}/stargazers`, } : undefined, repositories: repos ? getRepos(repos) : undefined, technologies, }; const webpageSchema = getWebPageSchema({ description: seo.description, locale: CONFIG.locales.defaultLocale, slug: asPath, title: seo.title, updateDate: dates.update, }); const articleSchema = getSinglePageSchema({ cover: `/projects/${id}.jpg`, dates, description: intro, id: 'project', kind: 'page', locale: CONFIG.locales.defaultLocale, slug: asPath, title, }); const schemaJsonLd = getSchemaJson([webpageSchema, articleSchema]); const sharingWidgetTitle = intl.formatMessage({ defaultMessage: 'Share', id: 'HKKkQk', description: 'SharingWidget: widget title', }); const tocTitle = intl.formatMessage({ defaultMessage: 'Table of Contents', description: 'PageLayout: table of contents title', id: 'eys2uX', }); return ( {page.title} {/*eslint-disable-next-line react/jsx-no-literals -- Name allowed */} {/*eslint-disable-next-line react/jsx-no-literals -- Content allowed */}